package fr.cephb.joperon.tool.altera;

import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.sleepycat.db.DatabaseException;

import fr.cephb.joperon.tool.OperonFactory;
import fr.cephb.joperon.core.Fid;
import fr.cephb.joperon.core.bio.Assembly;
import fr.cephb.joperon.core.bio.Chromosome;
import fr.cephb.joperon.core.db.SnpMapDB;
import fr.cephb.joperon.core.entities.MPanel;
import fr.cephb.joperon.core.entities.SnpMap;
import fr.cephb.joperon.core.entities.XPanel;

/**
 * A class wrapping the functions for ALTERA: getting the SNP in a region
 * @author operon
 *
 */
public class Altera
	{
	private Assembly assembly= Assembly.getDefaultAssembly();
	private Chromosome chromosome= null;
	private int start=0;
	private int end=0;
	private OperonFactory factory;
	private String filterFlags;
	List<MPanel> allMPanels= new ArrayList<MPanel>();
	private Set<MPanel> mPanels=new HashSet<MPanel>();
	private boolean selectedMPanelIndexes[]=new boolean[0];
	private boolean xpanelExists=false;
	
	/**
	 *  holds the information about the panel for a SNP
	 *
	 */
	public abstract class PanelInfo
		{
		public abstract String getSyn();
		public abstract String getFreq();
		}
	
	private class EmptyPanelInfo
		extends PanelInfo
		{
		@Override
		public String getSyn()
			{
			return "";
			}
		@Override
		public String getFreq()
			{
			return "";
			}
		}
	
	private class XPanelInfo
	extends PanelInfo
		{
		private XPanel xpane;
		private MPanel mpanel;
		XPanelInfo(MPanel mpanel,XPanel xpane)
			{
			this.mpanel=mpanel;
			this.xpane=xpane;
			}
		
		@Override
		public String getSyn()
			{
			return String.valueOf(this.mpanel.getSyn());
			}
		@Override
		public String getFreq()
			{
			return String.valueOf(xpane.getFrequence());
			}
		}
	
	/**
	 * 
	 * hold the informations about a SNP
	 *
	 */
	public class SNP
		{
		private SnpMap map;
		public SNP(SnpMap loc)
			{
			this.map=loc;
			}
		
		public SnpMap getMap() {
			return map;
			}
		public List<PanelInfo> getPanelInfo() throws DatabaseException
			{
			List<PanelInfo> panelinfo= new ArrayList<PanelInfo>();
			if(xpanelExists)
				{
				List<XPanel> xpanels= getFactory().getXPanel(getAssembly()).getConcatenedValues(getMap().getFid());
				for(int i=0;i< selectedMPanelIndexes.length &&
						xpanels!=null &&
						!xpanels.isEmpty();++i)
					{
					if(!selectedMPanelIndexes[i]) continue;
					panelinfo.add(new XPanelInfo(
						allMPanels.get(i),
						xpanels.get(i)
						));
					}
				}
			
			
			if(panelinfo.isEmpty()) panelinfo.add(new EmptyPanelInfo());
			return panelinfo;
			}
		}
	
	public void setMPanels(Set<MPanel> mpanels) {
		this.mPanels= mpanels;
		}
	
	 Set<MPanel> getMPanels() {
		return mPanels;
		}
	
	public void setAssembly(Assembly assembly) {
		this.assembly = assembly;
		}
	
	

	
	public Chromosome getChromosome() {
		return chromosome;
		}
	
	public void setChromosome(String s)
		{
		if(s==null) throw new IllegalArgumentException("Bad chromosome "+s);
		Chromosome c= Chromosome.getByName(s);
		if(c==null) throw new IllegalArgumentException("Bad chromosome "+s);
		this.chromosome = c;
		}
	
	public Assembly getAssembly() {
		return assembly;
	}
	
	public int getStart() {
		return start;
	}
	
	public int getEnd() {
		return end;
	}
	
	public void setStart(int start)
		{
		start=Math.max(0, start);
		start=Math.min(start, Fid.MAX_VALUE);
		this.start = start;
		}
	
	public void setEnd(int end)
		{
		end=Math.max(0, end);
		end=Math.min(end, Fid.MAX_VALUE);
		this.end = end;
		}
	
	public void setFactory(OperonFactory factory) {
		this.factory = factory;
		}
	
	public OperonFactory getFactory() {
		return factory;
		}
	
	public void setFilterFlags(String filterFlags)
		{
		this.filterFlags = filterFlags;
		if(this.filterFlags==null) this.filterFlags="";
		}
	
	public List<SNP> getSnps() throws DatabaseException
		{
		List<SNP> list= new ArrayList<SNP>();
		SnpMapDB.ValueIterator iter= getFactory().getSnpMap(getAssembly()).listBetween(
				getChromosome(), getStart(), getEnd());
		while(iter.hasNext())
			{
			list.add(new SNP(iter.next()));
			if(list.size()>= getMaxReturn()) break;
			}
		iter.close();
		return list;
		}
	
	public int getMaxReturn()
		{
		return 1000000;
		}
	
	public void preprocess() throws DatabaseException
		{
		this.allMPanels= getFactory().getMPanel().getValues();
		this.selectedMPanelIndexes=new boolean[allMPanels.size()];
		Arrays.fill(this.selectedMPanelIndexes, false);
		
		this.xpanelExists=getFactory().isXPanelExists(getAssembly());
		
		if(xpanelExists)
			{
			for(int i=0;i< this.allMPanels.size();++i)
				{
				selectedMPanelIndexes[i]=getMPanels().contains(this.allMPanels.get(i));
				}
			}
		}
	
	public void print(PrintWriter out)  throws DatabaseException
		{
		SnpMapDB.ValueIterator iter=null;
		long prevEnd=0;
		out.print("acn\tchr\td\tp\tq\tcontig\tori\tx\ty\tw\tflags\tcat\tca\tra\taa\tpanel\tfreq");
		for(int i=0;i< allMPanels.size();++i)
			{
			if(!selectedMPanelIndexes[i]) continue;
			out.print("\t"+this.allMPanels.get(i).getSyn()+"RA\tnAA\tAA\tnAB\tAB\tnBB\tBB\tnNN");
			}
		out.println();
		try
		{
		iter= getFactory().getSnpMap(getAssembly()).listBetween(
				getChromosome(), getStart(), getEnd());
		while(iter.hasNext())
			{
			SnpMap map=iter.next();
			out.print(map.getAcn()); out.print("\t");
			out.print(map.getBand()); out.print("\t");
			out.print(map.getChromosome()); out.print("\t");
			out.print(map.getP()-prevEnd); out.print("\t");
			prevEnd=map.getQ();
			out.print(map.getP()); out.print("\t");
			out.print(map.getQ()); out.print("\t");
			out.print(map.getCtg()); out.print("\t");
			out.print(map.getOrientation()); out.print("\t");
			out.print(map.getX()); out.print("\t");
			out.print(map.getY()); out.print("\t");
			out.print(map.getMw()); out.print("\t");
			out.print(map.getFlags()); out.print("\t");
			out.print(map.getCategory()); out.print("\t");
			out.print(map.getCa()); out.print("\t");
			out.print(map.getRa()); out.print("\t");
			out.print(map.getAa()); 
			
			
			if(xpanelExists)
				{
				List<XPanel> xpanels= getFactory().getXPanel(getAssembly()).getConcatenedValues(map.getFid());
				for(int i=0;i< allMPanels.size() && 
					xpanels!=null &&
					!xpanels.isEmpty();++i)
					{
					if(!selectedMPanelIndexes[i]) continue;
					XPanel xpanel= xpanels.get(i);
					out.print("\t");

					out.print(xpanel.getFrequence()); out.print("\t");
					char genotypes[]= xpanel.getAlleles();
					
					out.print("\t"+genotypes[0]+genotypes[0]+"\t");
					out.print(xpanel.getAa());
					
					out.print("\t"+genotypes[0]+genotypes[1]+"\t");
					out.print(xpanel.getAb());
					out.print("\t"+genotypes[1]+genotypes[1]+"\t");
					out.print(xpanel.getBb());
					out.print("\t");
					out.print(xpanel.getNn());
					}
				
				}
			
			out.println();
			}
			
			}
		catch(DatabaseException err)
			{
			err.printStackTrace(out);
			}
		catch(Throwable err)
			{
			err.printStackTrace(out);
			throw new DatabaseException(err);
			}
		finally
			{
			if(iter!=null) iter.close();
			}
		out.flush();
		
		
		}
	
}